#pragma once
#include <memory>
#include <iostream>
#include <string>
#include <fstream>
#include <cassert>
#include <sstream>
#include <atomic>
#include "../comm/timehelper.hpp"
#include "../comm/filehelper.hpp"
/*日志落地的位置
    ——标准输出 ——文件 ——文件的缺陷（数据过大，需要清理，不好管理） ——滚动数组
    实现思想:基类规定log 派生出StdoutSink FileSink  维护管理句柄(不需要每次写都打开文件、关闭文件)
    RollSink:curr_size大于max_size 就往新文件里写入数据 Date+Time命名文件标志
        维护的成员 max_size curr_size ofs文件管理句柄 basedirec

    工厂模式构建日志落地信息，做到创建和使用分离
    因为落地的参数不同，所以再引入可变参数
*/
namespace ns_logger
{
    class LogSink
    {
    public:
        LogSink() {}
        virtual ~LogSink() {}
        virtual void log(const char *data, size_t len) = 0;
    };

    using LogSinkPtr = std::shared_ptr<LogSink>;

    // 往标准输出写数据
    class StdoutSink : public LogSink
    {
    public:
        StdoutSink() = default;
        void log(const char *data, size_t len) override
        {
            std::cout.write(data, len);
        }
    };
    // 往指定的文件里写数据
    class FileSink : public LogSink
    {
    public:
        FileSink(const std::string &filename)
            : _filename(filename)
        {
            ns_helper::FileUntil::CreateDirectory(ns_helper::FileUntil::FilePath(_filename));
            // 创建文件
            _ofs.open(_filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }

        void log(const char *data, size_t len) override
        {
            // 往文件写数据
            _ofs.write(data, len);
            if (!_ofs.good())
                std::cout << "往文件写入:" << data << "失败" << std::endl;
        }
        ~FileSink()
        {
            _ofs.close();
        }

    private:
        // 保存句柄
        std::ofstream _ofs;
        std::string _filename;
    };

    // 往滚动数组中写入数据
    class RollSink : public LogSink
    {
    public:
        RollSink(const std::string &basedir, size_t max_len = 1024 * 1024 * 1024)
            : _max_len(max_len), _basedirec(basedir),curr_len(0),_name_count(0)
        {
            // 创建目录，打开文件，获取句柄
            ns_helper::FileUntil::CreateDirectory(ns_helper::FileUntil::FilePath(basedir));
        }
        void log(const char *data, size_t len) override
        {
            /*
                问题出在这里，数据是连成一块的
            */
            //std::cout<<"写入数据:"<<data;
            //std::cout<<"curr_len:"<<curr_len<<"-_max_len"<<_max_len<<std::endl;
            InitOfs();
            // 往流中写数据，并且更新curr_size
            _ofs.write(data, len);
            if (!_ofs.good())
                std::cout << "往文件写入:" << data << "失败" << std::endl;
            curr_len += len;
           
        }

    private:
        void InitOfs()
        {
            // 监测数据是否满了
            if (!_ofs.is_open()||curr_len >= _max_len)
            {
                // 创建文件，更新ofs,currlen
                std::string filename = CreateFilename();
                // std::cout<<"当前文件大小:"<<curr_len<<"创建新文件:"<<filename<<std::endl;
                _ofs.close();
                _ofs.open(filename, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                curr_len = 0;
            }
            return;
        }
        std::string CreateFilename()
        {
            time_t t = ns_helper::TimeUntil::GetTime();
            struct tm tm;
            localtime_r(&t, &tm);
            // 输入到字符串中
            std::stringstream ss;
            ss<<_basedirec;
            ss << tm.tm_year+1900;
            ss<<"-";
            ss<<tm.tm_mon+1;
            ss<<"-";
            ss<<tm.tm_mday;
            ss<<"_";
            ss<<tm.tm_hour;
            ss<<":";
            ss<<tm.tm_min;
            ss<<":";
            ss<<tm.tm_sec;
            ss<<"-"<<_name_count;
            ss<<".log";
            _name_count++;
            return ss.str();
        }
        RollSink(){
            _ofs.close();
        }

    private:
        size_t _max_len;
        size_t curr_len;
        std::atomic<int> _name_count;
        std::ofstream _ofs;
        std::string _basedirec;
    };

    enum class Gap{
        GAP_SECOND=0,
        GAP_MINUTE,
        GAP_HOUR,
        GAP_DAY
    };

    // 以时间滚动文件中写入数据
    class RollTimeSink : public LogSink
    {
    public:
        RollTimeSink(const std::string &basedir,Gap gap)
            :  _basedirec(basedir),_name_count(0),curr_gap(0)
        {
            // 创建目录,初始化时间间隔
            ns_helper::FileUntil::CreateDirectory(ns_helper::FileUntil::FilePath(basedir));
            switch (gap)
            {
                case Gap::GAP_SECOND :_gap_size=1;break;
                case Gap::GAP_MINUTE :_gap_size=60;break;
                case Gap::GAP_HOUR :_gap_size=60*60;break;
                case Gap::GAP_DAY :_gap_size=60*60*24;break;
            }
            std::string filename = CreateFilename();
            _ofs.open(filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
            curr_gap=time(nullptr)/_gap_size;
        }
        void log(const char *data, size_t len) override
        {
            InitOfs();
            // 往流中写数据，并且更新curr_size
            _ofs.write(data, len);
            if (!_ofs.good())
                std::cout << "往文件写入:" << *data << "失败" << std::endl;
        }

    private:
        void InitOfs()
        {
            time_t now=ns_helper::TimeUntil::GetTime();
            // 监测数据是否满了
            if ((now/_gap_size)!=curr_gap)
            {
                // 创建文件，更新ofs,currlen
                std::string filename = CreateFilename();
                _ofs.close();
                _ofs.open(filename, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                curr_gap=now/_gap_size;
            }
            return;
        }
        std::string CreateFilename()
        {
            time_t t = ns_helper::TimeUntil::GetTime();
            struct tm tm;
            localtime_r(&t, &tm);
            // 输入到字符串中
            std::stringstream ss;
            ss<<_basedirec;
            ss << tm.tm_year+1900;
            ss<<"-";
            ss<<tm.tm_mon+1;
            ss<<"-";
            ss<<tm.tm_mday;
            ss<<":";
            ss<<tm.tm_hour;
            ss<<":";
            ss<<tm.tm_min;
            ss<<":";
            ss<<tm.tm_sec;
            ss<<"-"<<_name_count;
            ss<<".log";
            std::cout<<ss.str()<<std::endl;
            _name_count++;
            return ss.str();
        }
        ~RollTimeSink(){
            _ofs.close();
        }

    private:
        size_t _name_count;
        std::ofstream _ofs;
        std::string _basedirec;
        size_t curr_gap;
        size_t _gap_size;
    };

    // 工厂模式的创建
    class SinkFoctory
    {
    public:
        template <typename SinkType, typename... Args>
        static LogSinkPtr CreateSink(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };

}
